package com.myk.game.gobangchess.ai.symmetric;

import java.util.*;
import java.util.stream.Collectors;

/**
 * AI控制器，使用Minimax算法结合启发式评估来寻找最佳走法。
 */
public class AiController {

    /**
     * 内部类，用于封装一个完整的走法及其评估分数。
     * 包含一个10元素的整数数组表示具体行动，以及用于排序和决策的分数。
     */
    private static class Move implements Comparable<Move> {
        int[] action = new int[10]; // [0,1]:落子; [2,3]:拉子1起点; [4,5]:拉子1终点; ...
        double layer1Score;         // 第一层（当前层）的启发式评估分数
        double layer2Score;         // 第二层（Minimax）的评估分数

        public Move() {
            Arrays.fill(action, -1);
            this.layer1Score = Double.NEGATIVE_INFINITY;
            this.layer2Score = Double.NEGATIVE_INFINITY;
        }

        /**
         * 默认排序规则：按第一层分数降序排列，分数高的优先。
         */
        @Override
        public int compareTo(Move other) {
            return Double.compare(other.layer1Score, this.layer1Score);
        }

        @Override
        public String toString() {
            return "Move{" +
                    "action=" + Arrays.toString(action) +
                    ", L1_Score=" + String.format("%.2f", layer1Score) +
                    ", L2_Score=" + String.format("%.2f", layer2Score) +
                    '}';
        }
    }

    /**
     * AI决策主入口函数。通过Minimax搜索找到最佳行动。
     * @param board 当前15x15棋盘状态。
     * @param aiPlayerColor AI所扮演的玩家颜色 (1:黑, -1:白)。
     * @param depth 搜索深度 (当前支持1或2)。
     * @param branchingFactor 在搜索的每一层，考虑的顶尖走法的数量。
     * @param coefficients 用于趋势打分的17个权重系数。
     * @return 一个包含10个整数的数组，描述最佳行动。
     */
    public int[] findBestMove(int[][] board, int aiPlayerColor, int depth, int branchingFactor, double[] coefficients) {
        // 1. 初始化游戏状态
        GameState initialState = GameStateGenerator.getGameState(board);
        initialState.currentPlayer = aiPlayerColor;
        initialState.totalPullsAllowed = 1; // 默认拉子次数为1
        List<List<int[]>> playerBlocks = initialState.allBlocks.get(aiPlayerColor);
        if (playerBlocks != null) {
            for (int i = 0; i < playerBlocks.size(); i++) {
                // 如果存在大小>=5的不对称棋块，则拉子次数为2
                if (!initialState.blockSymmetries.get(aiPlayerColor).get(i) && initialState.blockSizes.get(aiPlayerColor).get(i) >= 5) {
                    initialState.totalPullsAllowed = 2;
                    break;
                }
            }
        }

        // 2. 生成我方所有可能的走法，并在生成时直接进行第一层评估
        List<Move> myPossibleMoves = generateAndEvaluateMoves(initialState, aiPlayerColor, coefficients, branchingFactor);

        Move bestMove = new Move();

        // 3. 根据深度执行搜索策略
        switch (depth) {
            case 1: {
                // 深度为1：选择第一层启发式分数最高的走法
                bestMove = myPossibleMoves.stream()
                                          .max(Comparator.comparingDouble(m -> m.layer1Score))
                                          .orElse(new Move());
                break;
            }
            case 2: {
                // 深度为2：执行Minimax搜索
                Collections.sort(myPossibleMoves);
                List<Move> topMyMoves = myPossibleMoves.stream().limit(branchingFactor).collect(Collectors.toList());

                if (topMyMoves.isEmpty()) {
                    bestMove = new Move(); // 没有可选走法
                } else {
                    bestMove = topMyMoves.get(0); // 默认选择第一层分数最高的
                    double bestMinimaxScore = Double.NEGATIVE_INFINITY;

                    for (Move myMove : topMyMoves) {
                        // 应用我方走法，得到新状态
                        GameState stateAfterMyMove = applyMoveToState(initialState, myMove);
                        
                        if (CategoryScorer.scoreByCategory(stateAfterMyMove, aiPlayerColor) < -60000) {
                            myMove.layer2Score = Double.NEGATIVE_INFINITY;
                            continue;
                        }

                        // 切换到对手视角
                        int opponentColor = -aiPlayerColor;
                        stateAfterMyMove.currentPlayer = opponentColor;
                        stateAfterMyMove.totalPullsAllowed = 1; // 重置对手拉子次数
                        List<List<int[]>> opponentBlocks = stateAfterMyMove.allBlocks.get(opponentColor);
                        if (opponentBlocks != null) {
                            for (int i = 0; i < opponentBlocks.size(); i++) {
                                if (!stateAfterMyMove.blockSymmetries.get(opponentColor).get(i) && stateAfterMyMove.blockSizes.get(opponentColor).get(i) >= 5) {
                                    stateAfterMyMove.totalPullsAllowed = 2;
                                    break;
                                }
                            }
                        }

                        // 生成并评估对手所有可能的反击，这里branchingFactor设为1，因为我们只关心对手的最佳应对
                        List<Move> opponentPossibleMoves = generateAndEvaluateMoves(stateAfterMyMove, opponentColor, coefficients, 1);
                        
                        // 找到对手能获得的最高分
                        double maxOpponentScore = opponentPossibleMoves.stream()
                                .mapToDouble(m -> m.layer1Score)
                                .max()
                                .orElse(Double.NEGATIVE_INFINITY); // 如果对手无棋可走，我方大优

                        myMove.layer2Score = -maxOpponentScore;

                        if (myMove.layer2Score > bestMinimaxScore) {
                            bestMinimaxScore = myMove.layer2Score;
                            bestMove = myMove;
                        }
                    }
                }
                break;
            }
            default:
                System.out.println("错误：此版本的AiController仅支持depth=1或2。");
                return new Move().action;
        }
        
        return bestMove.action;
    }

    /**
     * 生成并评估一个玩家在给定状态下的所有可能走法，并应用剪枝。
     * @param state 当前游戏状态。
     * @param playerColor 当前行动的玩家颜色。
     * @param coefficients 评估函数权重。
     * @param branchingFactor 决定了要保留多少个最优走法，用于计算阈值。
     * @return 一个包含所有可能走法及其第一层评估分数的列表。
     */
    private List<Move> generateAndEvaluateMoves(GameState state, int playerColor, double[] coefficients, int branchingFactor) {
        List<Move> topMoves = new ArrayList<>();
        double scoreThreshold = Double.NEGATIVE_INFINITY;
        Set<String> visitedBoardHashes = new HashSet<>();
        int pullsAllowed = state.totalPullsAllowed;

        for (int r = 0; r < 15; r++) {
            for (int c = 0; c < 15; c++) {
                if (state.board[r][c] != 0) continue;

                // --- 步骤1: 执行落子，获取落子后的新状态 ---
                GameState stateAfterPlace = state.deepCopy();
                GameStateUpdater.addPiece(stateAfterPlace, r, c, playerColor);

                // --- 步骤2: 基于落子后的状态，判断剪枝条件所需的变量 ---
                // 检查落子后，落子方是否存在不对称棋块 (用于条件2和4)
                boolean hasAsymmetricBlockAfterPlace = false;
                List<List<int[]>> playerBlocksAfterPlace = stateAfterPlace.allBlocks.get(playerColor);
                if (playerBlocksAfterPlace != null) {
                    for (int i = 0; i < playerBlocksAfterPlace.size(); i++) {
                        if (!stateAfterPlace.blockSymmetries.get(playerColor).get(i)) {
                            hasAsymmetricBlockAfterPlace = true;
                            break;
                        }
                    }
                }

                // --- 步骤3: 评估“仅落子”这个动作 ---
                // 条件4: 如果满足后期拉子限制，"仅落子"是允许的("放弃拉子")，所以不剪枝。
                // 阈值剪枝2: 如果阈值>0且只有一次拉子机会，则必须拉对方棋子，所以"仅落子"被忽略。
                if (!(scoreThreshold > 0 && pullsAllowed == 1)) {
                    addMoveIfNew(topMoves, visitedBoardHashes, stateAfterPlace, new int[]{r, c, -1, -1, -1, -1, -1, -1, -1, -1}, playerColor, coefficients, branchingFactor, scoreThreshold);
                    scoreThreshold = updateScoreThreshold(topMoves, branchingFactor);
                }

                // --- 步骤4: 遍历并评估所有可能的拉子动作 ---
                List<int[][]> firstPulls = generatePulls(stateAfterPlace, new int[]{r, c});
                for (int[][] pull1 : firstPulls) {
                    // 如果是第一次拉子 (也是最后一次机会)
                    if (pullsAllowed == 1) {
                        int pulledPieceColor1 = stateAfterPlace.board[pull1[0][0]][pull1[0][1]];
                        // 阈值剪枝2: 阈值>0时必须拉对方棋子
                        if (scoreThreshold > 0 && pulledPieceColor1 == playerColor) continue;
                        // 条件4: 后期无不对称块时，必须拉对方棋子
                        if (stateAfterPlace.totalPieceCount >= 30 && !hasAsymmetricBlockAfterPlace && pulledPieceColor1 == playerColor) continue;
                        // 条件2: 有不对称块时，必须满足强制拉子要求
                        if (hasAsymmetricBlockAfterPlace && !isValidFinalPull(stateAfterPlace, pull1, playerColor)) continue;
                        // 条件3: 拉对方棋子但无效
                        if (pulledPieceColor1 != playerColor && isIneffectiveOpponentPull(stateAfterPlace, pull1, playerColor)) continue;
                    }

                    GameState stateAfterPull1 = stateAfterPlace.deepCopy();
                    // 条件1: 孤立移动
                    if (isIsolatedMove(stateAfterPull1, pull1)) continue;

                    applySinglePull(stateAfterPull1, pull1);
                    int[] action1 = {r, c, pull1[0][0], pull1[0][1], pull1[1][0], pull1[1][1], -1, -1, -1, -1};
                    addMoveIfNew(topMoves, visitedBoardHashes, stateAfterPull1, action1, playerColor, coefficients, branchingFactor, scoreThreshold);
                    scoreThreshold = updateScoreThreshold(topMoves, branchingFactor);

                    // 如果允许第二次拉子
                    if (pullsAllowed == 2) {
                        // 检查第二次拉子后的状态，判断不对称块 (用于条件2和4)
                        boolean hasAsymmetricBlockAfterPull1 = false;
                        List<List<int[]>> playerBlocksAfterPull1 = stateAfterPull1.allBlocks.get(playerColor);
                        if (playerBlocksAfterPull1 != null) {
                            for (int i = 0; i < playerBlocksAfterPull1.size(); i++) {
                                if (!stateAfterPull1.blockSymmetries.get(playerColor).get(i)) {
                                    hasAsymmetricBlockAfterPull1 = true;
                                    break;
                                }
                            }
                        }

                        List<int[][]> secondPulls = generatePulls(stateAfterPull1, new int[]{r, c});
                        for (int[][] pull2 : secondPulls) {
                            int pulledPieceColor2 = stateAfterPull1.board[pull2[0][0]][pull2[0][1]];
                            // 条件4: 后期无不对称块时，必须拉对方棋子
                            if (stateAfterPull1.totalPieceCount >= 30 && !hasAsymmetricBlockAfterPull1 && pulledPieceColor2 == playerColor) continue;
                            // 条件2: 有不对称块时，必须满足强制拉子要求
                            if (hasAsymmetricBlockAfterPull1 && !isValidFinalPull(stateAfterPull1, pull2, playerColor)) continue;
                            // 条件3: 拉对方棋子但无效
                            if (pulledPieceColor2 != playerColor && isIneffectiveOpponentPull(stateAfterPull1, pull2, playerColor)) continue;

                            GameState stateAfterPull2 = stateAfterPull1.deepCopy();
                            // 条件1: 孤立移动
                            if (isIsolatedMove(stateAfterPull2, pull2)) continue;

                            applySinglePull(stateAfterPull2, pull2);
                            int[] action2 = {r, c, pull1[0][0], pull1[0][1], pull1[1][0], pull1[1][1], pull2[0][0], pull2[0][1], pull2[1][0], pull2[1][1]};
                            addMoveIfNew(topMoves, visitedBoardHashes, stateAfterPull2, action2, playerColor, coefficients, branchingFactor, scoreThreshold);
                            scoreThreshold = updateScoreThreshold(topMoves, branchingFactor);
                        }
                    }
                }
            }
        }
        return topMoves;
    }

    /**
     * 辅助函数：如果一个走法产生的局面是新的，则评估它并加入列表。
     * @param moves 存储顶尖走法的列表。
     * @param visitedHashes 存储已访问局面的哈希集合。
     * @param finalState 走法执行后的最终状态。
     * @param action 描述走法的数组。
     * @param playerColor 当前玩家颜色。
     * @param coefficients 评估权重。
     * @param branchingFactor 列表容量。
     * @param scoreThreshold 当前的分数阈值。
     */
    private void addMoveIfNew(List<Move> moves, Set<String> visitedHashes, GameState finalState, int[] action, int playerColor, double[] coefficients, int branchingFactor, double scoreThreshold) {
        String boardHash = Arrays.deepToString(finalState.board);
        if (visitedHashes.contains(boardHash)) {
            return;
        }
        
        // --- 阈值剪枝条件1：基于分类分数的剪枝 ---
        double categoryScore = (double) CategoryScorer.scoreByCategory(finalState, playerColor);
        if (categoryScore <= scoreThreshold - 1000) {
            visitedHashes.add(boardHash); // 标记为已访问，即使被剪枝
            return; // 剪枝：分类分数过低，无需计算趋势分
        }

        visitedHashes.add(boardHash);
        Move move = new Move();
        move.action = action;
        
        // 评估分数
        if (Math.abs(categoryScore) > 60000) {
            move.layer1Score = categoryScore;
        } else {
            double trendScore = TrendScorer.calculateTrendScore(finalState, playerColor, coefficients);
            move.layer1Score = categoryScore + trendScore;
        }

        // 维护一个固定大小的、按分数排序的列表
        if (moves.size() < branchingFactor) {
            moves.add(move);
            Collections.sort(moves);
        } else if (move.layer1Score > moves.get(moves.size() - 1).layer1Score) {
            moves.remove(moves.size() - 1);
            moves.add(move);
            Collections.sort(moves);
        }
    }

    /**
     * 更新并返回新的分数阈值。
     * @param moves 当前的顶尖走法列表。
     * @param branchingFactor 列表容量。
     * @return 新的阈值（列表中最低的分数）。
     */
    private double updateScoreThreshold(List<Move> moves, int branchingFactor) {
        if (moves.size() < branchingFactor) {
            return Double.NEGATIVE_INFINITY;
        }
        return moves.get(moves.size() - 1).layer1Score;
    }

    /**
     * 检查一次拉子是否为“孤立移动”。
     * 条件1：若被拉的子其原先位置和目标位置，均满足周围不存在任何棋子，则该行动直接忽略。
     * @param stateBeforePull 拉子前的状态。
     * @param pull 拉子动作 {{fromR, fromC}, {toR, toC}}。
     * @return 如果是孤立移动则返回true。
     */
    private boolean isIsolatedMove(GameState stateBeforePull, int[][] pull) {
        int fromR = pull[0][0], fromC = pull[0][1];
        int toR = pull[1][0], toC = pull[1][1];

        boolean isIsolatedAtStart = true;
        for (int dr = -1; dr <= 1; dr++) {
            for (int dc = -1; dc <= 1; dc++) {
                if (dr == 0 && dc == 0) continue;
                int nr = fromR + dr, nc = fromC + dc;
                if (nr >= 0 && nr < 15 && nc >= 0 && nc < 15 && stateBeforePull.board[nr][nc] != 0) {
                    isIsolatedAtStart = false;
                    break;
                }
            }
            if (!isIsolatedAtStart) break;
        }
        
        if (!isIsolatedAtStart) {
            return false;
        }

        GameState tempState = stateBeforePull.deepCopy();
        applySinglePull(tempState, pull);

        boolean isIsolatedAtEnd = true;
        for (int dr = -1; dr <= 1; dr++) {
            for (int dc = -1; dc <= 1; dc++) {
                if (dr == 0 && dc == 0) continue;
                int nr = toR + dr, nc = toC + dc;
                if (nr >= 0 && nr < 15 && nc >= 0 && nc < 15 && tempState.board[nr][nc] != 0) {
                    isIsolatedAtEnd = false;
                    break;
                }
            }
            if (!isIsolatedAtEnd) break;
        }

        return isIsolatedAtEnd;
    }

    /**
     * 检查在最后一次拉子机会时，该拉子是否满足强制性要求。
     * 条件2：必须拉落子方的子，且满足(1)或(2)。
     * @param stateBeforePull 拉子前的状态。
     * @param pull 拉子动作。
     * @param playerColor 落子方颜色。
     * @return 如果满足条件则返回true。
     */
    private boolean isValidFinalPull(GameState stateBeforePull, int[][] pull, int playerColor) {
        int fromR = pull[0][0], fromC = pull[0][1];
        int toR = pull[1][0], toC = pull[1][1];
        int pulledPieceColor = stateBeforePull.board[fromR][fromC];

        if (pulledPieceColor != playerColor) {
            return false;
        }

        boolean belongsToAsymmetricBlock = false;
        List<List<int[]>> playerBlocks = stateBeforePull.allBlocks.get(playerColor);
        if (playerBlocks != null) {
            for (int i = 0; i < playerBlocks.size(); i++) {
                if (!stateBeforePull.blockSymmetries.get(playerColor).get(i)) {
                    for (int[] piecePos : playerBlocks.get(i)) {
                        if (piecePos[0] == fromR && piecePos[1] == fromC) {
                            belongsToAsymmetricBlock = true;
                            break;
                        }
                    }
                }
                if (belongsToAsymmetricBlock) break;
            }
        }
        if (belongsToAsymmetricBlock) {
            return true;
        }

        GameState tempState = stateBeforePull.deepCopy();
        GameStateUpdater.removePiece(tempState, fromR, fromC);

        List<List<int[]>> asymmetricBlocksSurroundings = new ArrayList<>();
        List<List<int[]>> currentBlocks = tempState.allBlocks.get(playerColor);
        if (currentBlocks != null) {
            for (int i = 0; i < currentBlocks.size(); i++) {
                if (!tempState.blockSymmetries.get(playerColor).get(i)) {
                    asymmetricBlocksSurroundings.add(tempState.blockSurroundingPoints.get(playerColor).get(i));
                }
            }
        }

        if (asymmetricBlocksSurroundings.isEmpty()) {
            return true;
        }

        for (List<int[]> surroundings : asymmetricBlocksSurroundings) {
            boolean targetFound = false;
            for (int[] point : surroundings) {
                if (point[0] == toR && point[1] == toC) {
                    targetFound = true;
                    break;
                }
            }
            if (!targetFound) {
                return false;
            }
        }

        return true;
    }

    /**
     * 检查一次拉对方棋子的动作是否无实际收益。
     * 条件3：被拉子原先所属棋块和新的所属棋块均满足：大小不为1且是对称的。
     * @param stateBeforePull 拉子前的状态。
     * @param pull 拉子动作。
     * @param playerColor 落子方颜色。
     * @return 如果是无效拉动则返回true。
     */
    private boolean isIneffectiveOpponentPull(GameState stateBeforePull, int[][] pull, int playerColor) {
        int fromR = pull[0][0], fromC = pull[0][1];
        int toR = pull[1][0], toC = pull[1][1];
        int opponentColor = -playerColor;

        // 1. 检查原先所属棋块
        List<Integer> originalBlockIndices = new ArrayList<>();
        List<List<int[]>> opponentSurroundings = stateBeforePull.blockSurroundingPoints.get(opponentColor);
        if (opponentSurroundings != null) {
            for (int i = 0; i < opponentSurroundings.size(); i++) {
                for (int[] point : opponentSurroundings.get(i)) {
                    if (point[0] == fromR && point[1] == fromC) {
                        originalBlockIndices.add(i);
                        break;
                    }
                }
            }
        }

        if (!originalBlockIndices.isEmpty()) {
            for (int index : originalBlockIndices) {
                int size = stateBeforePull.blockSizes.get(opponentColor).get(index);
                boolean isSymmetric = stateBeforePull.blockSymmetries.get(opponentColor).get(index);
                if (size == 1 || !isSymmetric) {
                    return false; // 原棋块不满足“大小不为1且对称”，所以此拉动可能有效
                }
            }
        }

        // 2. 模拟拉子并检查新的所属棋块
        GameState stateAfterPull = stateBeforePull.deepCopy();
        applySinglePull(stateAfterPull, pull);

        int newBlockIndex = -1;
        List<List<int[]>> newOpponentBlocks = stateAfterPull.allBlocks.get(opponentColor);
        if (newOpponentBlocks != null) {
            for (int i = 0; i < newOpponentBlocks.size(); i++) {
                for (int[] piece : newOpponentBlocks.get(i)) {
                    if (piece[0] == toR && piece[1] == toC) {
                        newBlockIndex = i;
                        break;
                    }
                }
                if (newBlockIndex != -1) break;
            }
        }

        if (newBlockIndex != -1) {
            int newSize = stateAfterPull.blockSizes.get(opponentColor).get(newBlockIndex);
            boolean newIsSymmetric = stateAfterPull.blockSymmetries.get(opponentColor).get(newBlockIndex);
            if (newSize == 1 || !newIsSymmetric) {
                return false; // 新棋块不满足“大小不为1且对称”，所以此拉动可能有效
            }
        }

        return true;
    }

    /**
     * (已修改) 生成在落子后所有可能的拉子动作。
     * 此版本会查找每个方向上的所有棋子，并为它们生成所有合法的拉动。
     * @param state 落子后的游戏状态。
     * @param placePos 落子位置 {row, col}。
     * @return 一个列表，每个元素是 int[2][2]，表示 {{fromR, fromC}, {toR, toC}}。
     */
    private List<int[][]> generatePulls(GameState state, int[] placePos) {
        List<int[][]> pulls = new ArrayList<>();
        int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1};
        int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1};

        for (int i = 0; i < 8; i++) { // 遍历8个方向
            // 沿当前方向扫描，找出所有棋子
            for (int k = 1; k < 15; k++) {
                int r = placePos[0] + k * dr[i];
                int c = placePos[1] + k * dc[i];

                if (r < 0 || r >= 15 || c < 0 || c >= 15) {
                    break; // 超出棋盘边界，停止扫描该方向
                }

                if (state.board[r][c] != 0) {
                    // 找到了一个可以拉的棋子
                    int[] pullCandidatePos = new int[]{r, c};
                    int candidateColor = state.board[r][c];
                    
                    // 查找该棋子所属棋块的大小
                    int blockSize = 0;
                    List<List<int[]>> blocks = state.allBlocks.get(candidateColor);
                    if (blocks != null) {
                        outerLoop:
                        for (int j = 0; j < blocks.size(); j++) {
                            for (int[] piecePos : blocks.get(j)) {
                                if (piecePos[0] == pullCandidatePos[0] && piecePos[1] == pullCandidatePos[1]) {
                                    blockSize = state.blockSizes.get(candidateColor).get(j);
                                    break outerLoop;
                                }
                            }
                        }
                    }

                    // 根据棋块大小确定拉子规则
                    if (blockSize == 1) { // 大小为1的棋块，可以拉任意格
                        for (int pullDist = 1; pullDist < 15; pullDist++) {
                            int toR = pullCandidatePos[0] - pullDist * dr[i];
                            int toC = pullCandidatePos[1] - pullDist * dc[i];
                            if (toR < 0 || toR >= 15 || toC < 0 || toC >= 15) break;
                            // 目标位置必须是空格，且不能是落子点本身
                            if (state.board[toR][toC] == 0 && !(toR == placePos[0] && toC == placePos[1])) {
                                pulls.add(new int[][]{pullCandidatePos, {toR, toC}});
                            }
                        }
                    } else { // 大小>1的棋块，只能拉一格（或跳过障碍物）
                        for (int pullDist = 1; pullDist < 15; pullDist++) {
                            int toR = pullCandidatePos[0] - pullDist * dr[i];
                            int toC = pullCandidatePos[1] - pullDist * dc[i];
                            if (toR < 0 || toR >= 15 || toC < 0 || toC >= 15) break;
                            // 目标位置必须是空格，且不能是落子点本身
                            if (state.board[toR][toC] == 0 && !(toR == placePos[0] && toC == placePos[1])) {
                                pulls.add(new int[][]{pullCandidatePos, {toR, toC}});
                                break; // 找到第一个空位就停止
                            }
                        }
                    }
                }
            }
        }
        return pulls;
    }

    /**
     * 将一个完整的走法应用到游戏状态上，返回一个新的状态对象。
     * @param originalState 原始状态。
     * @param move 要应用的走法。
     * @return 应用走法后的新GameState对象。
     */
    private GameState applyMoveToState(GameState originalState, Move move) {
        GameState newState = originalState.deepCopy();
        int playerColor = newState.currentPlayer;

        GameStateUpdater.addPiece(newState, move.action[0], move.action[1], playerColor);

        if (move.action[2] != -1) {
            applySinglePull(newState, new int[][]{{move.action[2], move.action[3]}, {move.action[4], move.action[5]}});
        }

        if (move.action[6] != -1) {
            applySinglePull(newState, new int[][]{{move.action[6], move.action[7]}, {move.action[8], move.action[9]}});
        }
        
        return newState;
    }

    /**
     * 辅助函数：在给定的GameState上执行一次拉子操作。
     */
    private void applySinglePull(GameState state, int[][] pull) {
        int fromR = pull[0][0], fromC = pull[0][1];
        int toR = pull[1][0], toC = pull[1][1];
        int pieceColor = state.board[fromR][fromC];
        GameStateUpdater.removePiece(state, fromR, fromC);
        GameStateUpdater.addPiece(state, toR, toC, pieceColor);
    }


    public static void main(String[] args) {
        // int[][] board = {
        //     {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0,-1, 0, 0,-1, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 1, 0,-1, 0, 0, 1, 1, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,-1, 0},
        //     {0, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0},
        //     {1,-1, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0},
        //     {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0,-1, 1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0,-1, 1, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        // };
        int[][] board = {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        };
        // int[][] board = {
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        // };
        // int[][] board = {
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0},
        //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        // };
        double[] coefficients = {
            2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 3.0, 4.0, 4.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 1.5, 0.1
        };

        System.out.println("Initial Board:");
        BoardPrinter.printBoardWithIndices(board);
        System.out.println("----------------------------------------");

        AiController ai = new AiController();
        int aiPlayerColor = 1;
        int searchDepth = 2; //控制难度 1和2
        int branchingFactor = 50;

        System.out.println("AI is thinking (Player " + aiPlayerColor + ", Depth: " + searchDepth + ", Branching: " + branchingFactor + ", No Alpha-Beta)...");
        
        int[] bestMove = ai.findBestMove(board, aiPlayerColor, searchDepth, branchingFactor, coefficients);

        System.out.println("\n--- AI Decision ---");
        System.out.println("Best move found (raw array): " + Arrays.toString(bestMove));
        System.out.println("Place piece at: (" + bestMove[0] + ", " + bestMove[1] + ")");
        if (bestMove[2] != -1) {
            System.out.println("Pull 1: from (" + bestMove[2] + ", " + bestMove[3] + ") to (" + bestMove[4] + ", " + bestMove[5] + ")");
        }
        if (bestMove[6] != -1) {
            System.out.println("Pull 2: from (" + bestMove[6] + ", " + bestMove[7] + ") to (" + bestMove[8] + ", " + bestMove[9] + ")");
        }
    }
}